#include <cstddef>
#include <cstring>
#include "napi/JsFunction.h"
#include "napi/NapiHelper.h"
#include "napi/native_api.h"
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <node_api.h>
#include <node_api_types.h>
#include <string>
#include <thread>
#include "napi/native_common.h"

#include "hilog/log.h"


napi_ref jsCallBack;
napi_deferred promise;

struct Md5Context {
    // 异步 worker
    napi_async_work work;

    // 对应 JS 端的 callback 函数
    napi_ref callback;

    // 对应 JS 端的 promise 对象
    napi_deferred promise;

    // 传递进来的参数
    std::string params;
    // 计算后的结果
    std::string result;
};


// 在子线程中执行
static void doInBackground(napi_env env, void *data) {

    Md5Context *md5Context = (Md5Context *)data;

    // 模拟耗时操作，进行 MD5 计算
    std::string md5 = "哈哈没想到吧我干";

    // 计算后的 MD5 字存储到 result 中
    md5Context->result = md5;

    // 模拟耗时操作，让当前线程休眠 3 秒钟
    std::this_thread::sleep_for(std::chrono::seconds(3));
}

// 切换到主线程
static void onPostExecutor(napi_env env, napi_status status, void *data) {
    Md5Context *md5Context = (Md5Context *)data;
    napi_value returnValue;
    if (napi_ok !=
        napi_create_string_utf8(env, md5Context->result.c_str(), md5Context->result.length(), &returnValue)) {
        delete md5Context;
        md5Context = nullptr;
        napi_throw_error(env, "-111", "napi_create_string_utf8: error");
        return;
    }

    // 以 promise 的形式回调数据
    if (napi_ok != napi_resolve_deferred(env, md5Context->promise, returnValue)) {
        delete md5Context;
        md5Context = nullptr;
        napi_throw_error(env, "-111", "napi_resolve_deferred error");
    }

    // 删除异步任务并释放资源
    napi_delete_async_work(env, md5Context->work);
    delete md5Context;
    md5Context = nullptr;
}

static napi_value getStringFromNativeByPromiseSync(napi_env env, napi_callback_info info) {
    // 1、从 info 中读取 JS 传递过来的参数放入 args 里
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1001", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、读取传入的参数类型
    napi_valuetype stringType = napi_undefined;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1002", "napi_typeof string error");
        return nullptr;
    }

    // 3、传入的 string 如果为 null 或者 undefined 则抛异常
    if (napi_null == stringType || napi_undefined == stringType) {
        napi_throw_error(env, "-1003", "input params null or undefined");
        return nullptr;
    }

    // 4、读取传入的 string 内容长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1004", "get string length error");
        return nullptr;
    }

    // 5、判断传入的 string 长度是否符合
    if (0 == length) {
        napi_throw_error(env, "-1005", "string length can't be zero");
        return nullptr;
    }

    // 6、读取传入的 string 长度读取内容
    char *buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_get_value_string_utf8 string error");
        return nullptr;
    }
    napi_value returnValue = nullptr;
    // promise 的回调方式，创建一个 Promise 的引用
    napi_deferred promise;
    if (napi_ok != napi_create_promise(env, &promise, &returnValue)) {
        napi_throw_error(env, "-11", "napi_create_promise error");
        return nullptr;
    }

    napi_value lastReturnValue;

    std::string reulstStr = "这个是返回的值同步哈";

    if (napi_ok != napi_create_string_utf8(env, reulstStr.c_str(), reulstStr.length(), &lastReturnValue)) {
        napi_throw_error(env, "-111", "napi_resolve_deferred error");
        return nullptr;
    }

    // 以 promise 的形式回调数据
    if (napi_ok != napi_resolve_deferred(env, promise, lastReturnValue)) {
        napi_throw_error(env, "-111", "napi_resolve_deferred error");
        return nullptr;
    }
    

    return returnValue;
}


static napi_value getStringFromNativeByPromise(napi_env env, napi_callback_info info) {
    // 1、从 info 中读取 JS 传递过来的参数放入 args 里
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1001", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、读取传入的参数类型
    napi_valuetype stringType = napi_undefined;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1002", "napi_typeof string error");
        return nullptr;
    }

    // 3、传入的 string 如果为 null 或者 undefined 则抛异常
    if (napi_null == stringType || napi_undefined == stringType) {
        napi_throw_error(env, "-1003", "input params null or undefined");
        return nullptr;
    }

    // 4、读取传入的 string 内容长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1004", "get string length error");
        return nullptr;
    }

    // 5、判断传入的 string 长度是否符合
    if (0 == length) {
        napi_throw_error(env, "-1005", "string length can't be zero");
        return nullptr;
    }

    // 6、读取传入的 string 长度读取内容
    char *buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_get_value_string_utf8 string error");
        return nullptr;
    }
    napi_value returnValue = nullptr;

    // 8、创建一个异步线程需要的数据 model，把传递过来的参数加入进去做下缓存
    auto context = new Md5Context();
    context->params = buffer;

    // promise 的回调方式，创建一个 Promise 的引用
    napi_deferred promise;
    if (napi_ok != napi_create_promise(env, &promise, &returnValue)) {
        delete[] buffer;
        delete context;
        buffer = nullptr;
        context = nullptr;

        napi_throw_error(env, "-11", "napi_create_promise error");
        return nullptr;
    }

    // 缓存 promise
    context->promise = promise;


    // 10、创建一个异步任务
    napi_async_work asyWork;

    napi_value resourceName;
    if (napi_ok != napi_create_string_utf8(env, "GetMd5", NAPI_AUTO_LENGTH, &resourceName)) {
        delete[] buffer;
        delete context;
        buffer = nullptr;
        context = nullptr;
        napi_throw_error(env, "-11", "napi_create_string_utf8 resourceName error");
        return nullptr;
    }


    napi_status status =
        napi_create_async_work(env, nullptr, resourceName, doInBackground, onPostExecutor, (void *)context, &asyWork);
    if (napi_ok != status) {
        delete[] buffer;
        delete context;
        buffer = nullptr;
        context = nullptr;

        napi_throw_error(env, "-11", "napi_create_async_work error");
        return nullptr;
    }

    // 11、保存异步任务
    context->work = asyWork;

    // 12、添加进异步队列
    napi_queue_async_work(env, asyWork);


    return returnValue;
}

static napi_value getStringFromNative(napi_env env, napi_callback_info info) {
    // 1、从 info 中读取 JS 传递过来的参数放入 args 里
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1001", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、读取传入的参数类型
    napi_valuetype stringType = napi_undefined;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1002", "napi_typeof string error");
        return nullptr;
    }

    // 3、传入的 string 如果为 null 或者 undefined 则抛异常
    if (napi_null == stringType || napi_undefined == stringType) {
        napi_throw_error(env, "-1003", "input params null or undefined");
        return nullptr;
    }

    // 4、读取传入的 string 内容长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1004", "get string length error");
        return nullptr;
    }

    // 5、判断传入的 string 长度是否符合
    if (0 == length) {
        napi_throw_error(env, "-1005", "string length can't be zero");
        return nullptr;
    }

    // 6、读取传入的 string 长度读取内容
    char *buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_get_value_string_utf8 string error");
        return nullptr;
    }

    // 7、读取 JS 有没有传递 callback，如果 callback 为 null 就表示是 promise 的回调方式
    napi_valuetype callbackType = napi_undefined;
    napi_status callbackStatus = napi_typeof(env, args[1], &callbackType);
    if (napi_ok != callbackStatus && napi_invalid_arg != callbackStatus) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1004", "napi_typeof function error");
        return nullptr;
    }

    napi_ref callbackjs;
    if (napi_ok != napi_create_reference(env, args[1], 1, &callbackjs)) {
        napi_throw_error(env, "-11", "napi_create_reference error");
        return nullptr;
    }

    // 准备好callback的值
    napi_value returnValue;
    std::string result = "这个是回调的string";
    napi_create_string_utf8(env, result.c_str(), result.length(), &returnValue);

    napi_value callback;
    if (napi_ok != napi_get_reference_value(env, callbackjs, &callback)) {
        napi_throw_error(env, "-111", "napi_get_reference_value error");
        return nullptr;
    }

    napi_value tempValue;
    // 返回一个 这个是回调的方法
    napi_call_function(env, nullptr, callback, 1, &returnValue, &tempValue);

    napi_delete_reference(env, callbackjs);

    return nullptr;
}

static napi_value putStringArray(napi_env env, napi_callback_info info) {
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

/**
 * 将一个int数组传入
 * @param env
 * @param info
 * @return
 */
static napi_value putIntArrayBuffer(napi_env env, napi_callback_info info) {
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);


    int32_t *buff_data = NULL;
    size_t byte_length = 0;
    napi_value buff = args[0];


    napi_get_arraybuffer_info(env, buff, (void **)&buff_data, &byte_length);

    int32_t sum = 0;

    for (int i = 0; i < byte_length / sizeof(int32_t); ++i) {
        sum += *(buff_data + i);
        OH_LOG_INFO(LOG_APP, LOG_TAG, "\n getIntArray :%d = %d", i, *(buff_data + i));
    }
    return nullptr;
}


static napi_value putInt(napi_env env, napi_callback_info info) {
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    // 传递的参数中获取 此时传递的参数都已经到了 args中
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    int64_t result;

    napi_get_value_int64(env, args[0], &result);

    return nullptr;
}

/**
 * 传递一个String进来
 * @param env env
 * @param info info
 */
static napi_value putString(napi_env env, napi_callback_info info) {

    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    // 传递的参数中获取 此时传递的参数都已经到了 args中
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char buf[256];
    napi_valuetype valuetype0;
    size_t length;
    napi_typeof(env, args[0], &valuetype0);
    napi_get_value_string_utf8(env, args[0], buf, sizeof(buf), &length);

    OH_LOG_INFO(LOG_APP, LOG_TAG, "get String :%s", buf);
    return nullptr;
}

/**
 * 传递一个boolean到c层
 * @param env
 * @param info
 * @return
 */
static napi_value putBoolean(napi_env env, napi_callback_info info) {
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    // 传递的参数中获取 此时传递的参数都已经到了 args中
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    bool result;
    napi_get_value_bool(env, args[0], &result);


    OH_LOG_INFO(LOG_APP, LOG_TAG, "get boolean :%d", result);
    return nullptr;
}

/**
 * add ArkTs上层需要调用的方法
 * @param env 固定参数 env
 * @param info 固定参数 info
 * @return 必须要返回的参数
 */
static napi_value Add(napi_env env, napi_callback_info info) {
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;
}

static napi_value registerFunction(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2];
    napi_status status;
    
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    if (argc != 2) {
        napi_throw_type_error(env, NULL, "Wrong number of arguments");
        return nullptr;
    }

    napi_valuetype valuetype;
    status = napi_typeof(env, args[0], &valuetype);
    if (status != napi_ok) {
        return nullptr;
    }
    if (valuetype != napi_string) {
        napi_throw_type_error(env, NULL, "Wrong arguments");
        return nullptr;
    }

    auto jsArg = args[0];
    size_t len = 0;
    status = napi_get_value_string_utf8(env, jsArg, nullptr, 0, &len);
    std::string functionName = "";
    functionName.resize(len, '\0');
    status = napi_get_value_string_utf8(env, jsArg, (char *)functionName.data(), functionName.size() + 1, &len);

    napi_valuetype functionType;
    status = napi_typeof(env, args[1], &functionType);
    if (status != napi_ok) {
        return nullptr;
    }
    if (functionType != napi_function) {
        napi_throw_type_error(env, NULL, "Wrong arguments");
        return nullptr;
    }

    napi_ref fucRef;
    NAPI_CALL(env, napi_create_reference(env, args[1], 1, &fucRef));

    char *name = new char[functionName.length() + 1];
    strcpy(name, functionName.c_str());
    JsFunction *jsFunction = new JsFunction(name, env, fucRef);
    
    // 将这个jsfunction保存到数组只中即可。
//    NapiHelper::GetInstance().registerNapiFunction(name, jsFunction);

//    std::string testParam = "fasdfasf";
//    NapiHelper::GetInstance().getFunction("NapiHelper.testDemo").invoke<void>(testParam);
    
    NapiHelper::GetInstance().registerNapiFunction(name, jsFunction);

    return nullptr;
}

napi_value cPlusCallJsFunction(napi_env env, napi_callback_info info) {
    
    // 调用无参无返回函数
    NapiHelper::GetInstance().getFunction("NapiJsFunction.callFunction").invoke<void>();
    
    // 调用有参无返回
    std::string params1 = "有参无返回的参数";
    NapiHelper::GetInstance().getFunction("NapiJsFunction.callFunctionHaveParams").invoke<void>(params1);
    
    // 调用无参有返回
    std::string version = NapiHelper::GetInstance().getFunction("NapiJsFunction.getVerionCode").invoke<std::string>();
    
    // 调用有参有返回
    std::string params2 = "调用有参有返回";
    std::string version2 = NapiHelper::GetInstance().getFunction("NapiJsFunction.getVerionCodeHaveParams").invoke<std::string>(params2);
    
    return nullptr;
}


EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"putString", nullptr, putString, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"putBoolean", nullptr, putBoolean, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"putInt", nullptr, putInt, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"putIntArrayBuffer", nullptr, putIntArrayBuffer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getStringFromNative", nullptr, getStringFromNative, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getStringFromNativeByPromise", nullptr, getStringFromNativeByPromise, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"getStringFromNativeByPromiseSync", nullptr, getStringFromNativeByPromiseSync, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"registerFunction", nullptr, registerFunction, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"cPlusCallJsFunction", nullptr, cPlusCallJsFunction, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }